#include "stdafx.h"
#include "page_property_map.hpp"
#include <boost/spirit.hpp>
#include <iostream>
#include <fstream>
#include <strstream>
#include <boost/bind.hpp>

namespace FndGame2D {
  using boost::bind ;
  using namespace boost::spirit ;

  template<typename T>
  class value_writer :
    public boost::static_visitor<void> {
  public:
    void operator()(int i) const {
      m_out << i ;
    }
    void operator()(double d) const {
      m_out << d ;

    }
    void operator()(bool b) const {
      m_out << (b ? "true" : "false") ;
    
    } 
    void operator()(const std::string& s) const {
      m_out << "\"" << s << "\"";

    }
    value_writer(T& o) : m_out(o) {}
    T& m_out ;
  } ;
  template<typename T> value_writer<T>  make_value_writer(T& t) {
    return value_writer<T>(t) ;
  }

  struct page_file_grammar : public grammar<page_file_grammar> {
    template <typename ScannerT>
    struct definition {
      definition(const page_file_grammar& self) {
	total_info = (* group)[bind(&page_file_grammar::print_info, self, _1,_2)] ;
	group = comment | (header>> +(key >> '=' >> value >>!comment)) ;
	value = (bool_type | real_type | integer_type |
		 string_type) ;
	key = (+(alnum_p | alpha_p | '_'))[bind(&page_file_grammar::set_group, boost::cref(self), _1,_2)] ;
	header = ch_p('[') >> key[bind(&page_file_grammar::set_key, boost::cref(self), _1,_2)]
			   >> ']' ;
	bool_type = (str_p("true") | str_p("false"))
	  [bind(&page_file_grammar::add_bool, boost::cref(self), _1,_2)] ;
	integer_type = int_p
	  [bind(&page_file_grammar::add_int, boost::cref(self), _1)] ;
	real_type = strict_real_p
	  [bind(&page_file_grammar::add_real, boost::cref(self), _1)] ;
	string_type = confix_p('"', (*c_escape_ch_p)[bind(&page_file_grammar::add_string, boost::cref(self), _1,_2)]
			       , '"') ;
	comment = comment_p("//") ;
      }
      const rule<ScannerT>& start() {return total_info ;}
      rule<ScannerT> total_info, group, key, value, comment, header ;
      rule<ScannerT> bool_type, real_type, integer_type, string_type ;
    } ;
    void print_info(const char* b, const char* e) const {
      std::string info(b,e) ;
      pantheios::log_INFORMATIONAL(info, " is what's print\n") ;
    }
    void add_real(double d) const {
      ppm.set_value(key, group, d) ;
    }
    void add_int(int i) const {
      ppm.set_value(key, group,i) ;
    }
    void add_bool(const char* b, const char* e) const {
      std::string s(b,e) ;
      ppm.set_value(key, group, s == "true") ;
    }
    void add_string(const char* b, const char* e) const {
      ppm.set_value(key, group, std::string(b,e)) ;
    }
    void set_key(const char* b, const char* e) const {
      key = std::string(b,e) ;
    }
    void set_group(const char* b, const char* e) const {
      group = std::string(b,e) ;
    }
    page_property_map& ppm ;
    page_file_grammar(page_property_map& p) : ppm(p) {}
    mutable std::string group ;
    mutable std::string key ;
  } ;


  bool page_property_map::load_info_from_file(const char* file_name) {
    std::ifstream file_in(file_name) ;
    if(!file_in) return false ;
    std::ostrstream str_file ;
    str_file << file_in.rdbuf() << '\0' ;
    file_in.close() ;
    page_file_grammar g(*this) ;
    parse_info<> info = parse(str_file.str(), g, space_p) ;
    if(info.hit) return true ;
    else {
      pantheios::log_DEBUG("parse " , file_name, " falied\n") ;
      return false ;
    }
  }

  void page_property_map::save_info_to_file(const char* file_name) {
    std::fstream f_out(file_name, std::ios::out | std::ios::trunc) ;
    if(!f_out) {
      pantheios::log_DEBUG(file_name, " could not created, (save_info_to_file)\n") ;
      return ;
    }
    real_impl_map::iterator it_b, it_e ;
    for(it_b = m_values.begin(), it_e = m_values.end() ;
	it_b != it_e ;
	++it_b) {
      f_out << '[' << (*it_b).first << "]\n" ;
      impl_map::iterator i_b = (*it_b).second.begin(), i_e = (*it_b).second.end() ;
      for( ; i_b != i_e ; ++i_b) {
	f_out << (*i_b).first << " = " ;
	boost::apply_visitor(make_value_writer(f_out),(*i_b).second)  ;
	f_out <<"\n" ;
      }
      f_out << "\n" ;
    }
    f_out.close() ;
  }
}
